#ifndef BASIC_HPP_INCLUDED
#define BASIC_HPP_INCLUDED

/**************************************************************************************************
 *
 * This file contains the basic struct, class and function of \code namespace betterShooter.
 *
 **************************************************************************************************/

#include "tool_math.hpp"

namespace shooter
{
namespace basic
{

  /**
   * @brief Enum of error type.
   */
  enum Error_e
  {
    ERR_None = 0,
    ERR_WrongClass,
    ERR_XML_WrongFile,
    ERR_XML_WrongNode,
    ERR_XML_WrongAttr,
  };

  /**
   * @brief  Class to represent a rectangular coordinate.
   *
   * @note   1. Parallel class with \code polor_coor_t.
   *         2. A base class.
   */
  template <class T>
  struct rect_coor_t // Rectangular coordinates
  {
    T x_, y_, z_;

    rect_coor_t(): x_(0), y_(0), z_(0){}
    rect_coor_t(T x0, T y0, T z0): x_(x0), y_(y0), z_(z0){}

    rect_coor_t(const rect_coor_t & copy): x_(copy.x_), y_(copy.y_), z_(copy.z_){}
    const rect_coor_t & operator=(const rect_coor_t & copy)
    {
      x_ = copy.x_; y_ = copy.y_; z_ = copy.z_;
      return (*this);
    }
    virtual ~rect_coor_t(){}

    /* Overload operator */
    explicit const rect_coor_t operator*(double ratio) const
    {
      rect_coor_t tmp(x_*ratio, y_*ratio, z_*ratio);
      return tmp;
    }
    const rect_coor_t operator+(const rect_coor_t & ele) const
    {
      rect_coor_t tmp(this->x_+ele.x_, this->y_+ele.y_, this->z_+ele.z_);
      return tmp;
    }
    const rect_coor_t operator-(const rect_coor_t & ele) const
    {
      rect_coor_t tmp(this->x_-ele.x_, this->y_-ele.y_, this->z_-ele.z_);
      return tmp;
    }
    bool operator==(const rect_coor_t & ele) const
    {
      return (x_==ele.x_) && (y_==ele.y_) && (z_==ele.z_);
    }
    bool operator!=(const rect_coor_t & ele) const
    {
      return !((x_==ele.x_) && (y_==ele.y_) && (z_==ele.z_));
    }

    /* Others */
    void set(T x0, T y0, T z0)
    {
      x_ = x0; y_ = y0; z_ = z0;
    }
  };

  /**
   * @brief  Class to represent a polor coordinate.
   *
   * @note   1. Parallel class with \code rect_coor_t.
   *         2. A base class.
   */
  template <class T>
  struct polor_coor_t // Polor coordinates
  {
    T rho_, alpha_, beta_;

    polor_coor_t(): rho_(0), alpha_(0), beta_(0){}
    polor_coor_t(T rho0, T alpha0, T beta0): rho_(rho0), alpha_(alpha0), beta_(beta0){}

    polor_coor_t(const polor_coor_t & copy): rho_(copy.rho_), alpha_(copy.alpha_), beta_(copy.beta_){}
    const polor_coor_t & operator=(const polor_coor_t & copy)
    {
      rho_ = copy.rho_; alpha_ = copy.alpha_; beta_ = copy.beta_;
      return (*this);
    }
    virtual ~polor_coor_t(){}

    /* Overload operator */
    explicit const polor_coor_t operator*(double ratio) const
    {
      polor_coor_t tmp(rho_*ratio, alpha_, beta_);
      return tmp;
    }
    bool operator==(const polor_coor_t & ele) const
    {
      return (rho_==ele.rho_) && (alpha_==ele.alpha_) && (beta_==ele.beta_);
    }
    bool operator!=(const polor_coor_t & ele) const
    {
      return !((rho_==ele.rho_) && (alpha_==ele.alpha_) && (beta_==ele.beta_));
    }

    /* Others */
    void set(T rho0, T alpha0, T beta0)
    {
      rho_ = rho0; alpha_ = alpha0; beta_ = beta0;
    }
  };

  /**
   * @brief  Class to represent a coordinate.
   *
   * @note   1. Contain \code rect_coor_t and \code polor_coor_t at the same time.
   *         2. If you update one type of coor e.g. \code rect_coor_t, the other
   *            one \code polor_coor_t will not follow immediately, instead it
   *            will only update when you access it.
   *         3. Parallel class with \code vector_t.
   *         4. A base class.
   */
  template <class T = double>
  class coor_t
  {
  public:
    /* Constructor */
    coor_t(): state_(S_Rect){}
    coor_t(T x0, T y0, T z0): state_(S_Rect),rect_(x0, y0, z0){}
    coor_t(T rho0, T alpha0, T beta0, int dummy): state_(S_Polor), polor_(rho0, alpha0, beta0)
    {
      dummy = dummy; // not used, just to classify overload function.
    }

    coor_t(const coor_t & copy): state_(copy.state_), rect_(copy.rect_), polor_(copy.polor_){}
    const coor_t & operator=(const coor_t & copy)
    {
      rect_ = copy.rect_;
      polor_ = copy.polor_;
      state_ = copy.state_;
      return (*this);
    }
    virtual ~coor_t(){}

    int state() const
    {
      return state_;
    }
    rect_coor_t<T> & rect() const
    {
      if(state_ == S_Polor)
      {
        _polorToRect();
      }
      state_ = S_Rect;
      return rect_;
    }
    polor_coor_t<T> & polor() const
    {
      if(state_ == S_Rect)
      {
        _rectToPolor();
      }
      state_ = S_Polor;
      return polor_;
    }

  protected:
    void _rectToPolor() const
    {
      T xy = tool_math::cal_mag(rect_.x_, rect_.y_);
      polor_.rho_ = tool_math::cal_mag(xy, rect_.z_);
      polor_.alpha_ = (rect_.y_ == 0)? 0: (rect_.x_ == 0)? tool_math::basic_parms<0>::C_Pi2: std::atan(rect_.y_ / rect_.x_);
      polor_.beta_ = (xy == 0)? 0: (rect_.z_ == 0)? tool_math::basic_parms<0>::C_Pi2: std::atan(xy / rect_.z_);
    }
    void _polorToRect() const
    {
      rect_.x_ = polor_.rho_ * std::sin(polor_.beta_) * std::cos(polor_.alpha_);
      rect_.y_ = polor_.rho_ * std::sin(polor_.beta_) * std::sin(polor_.alpha_);
      rect_.z_ = polor_.rho_ * std::cos(polor_.beta_);
      rect_.x_ = (rect_.x_ < 1e-10)? 0: rect_.x_;
      rect_.y_ = (rect_.y_ < 1e-10)? 0: rect_.x_;
      rect_.z_ = (rect_.z_ < 1e-10)? 0: rect_.z_;
    }

    enum state_e
    {
      S_Rect = 0,
      S_Polor,
    };
    mutable state_e state_;
    mutable rect_coor_t<T> rect_;
    mutable polor_coor_t<T> polor_;
  };

  /**
   * @brief  Class to represent a coordinate.
   *
   * @note   1. Parallel class with \code coor_t.
   *         2. A base class.
   */
  template <class T = double>
  struct vector_t: public coor_t<T>
  {
    /* Constructor */
    vector_t(){}
    vector_t(T x0, T y0, T z0): coor_t<T>(x0, y0, z0){}
    vector_t(T v0, T alpha0, T beta0, int dummy): coor_t<T>(v0, alpha0, beta0, dummy){}

    vector_t(const coor_t<T> & copy): coor_t<T>(copy){}
    const vector_t & operator=(const coor_t<T> & copy)
    {
      this->rect_ = copy.rect_;
      this->polor_ = copy.polor_;
      this->state_ = copy.state_;
      return (*this);
    }
    virtual ~vector_t(){}

    /* Overload operator */
    const vector_t operator^(const vector_t & ele) const // Cross product.
    {
      double x = this->rect().y_*ele.rect().z_ - this->rect().z_*ele.rect().y_;
      double y = this->rect().x_*ele.rect().z_ - this->rect().z_*ele.rect().x_;
      double z = this->rect().x_*ele.rect().y_ - this->rect().y_*ele.rect().z_;
      return vector_t(x, y, z);
    }
    double operator*(const vector_t & ele) const // Dot product.
    {
      return this->rect().x_*ele.rect().x_ + this->rect().z_*ele.rect().z_ + this->rect().y_*ele.rect().y_;;
    }
  };

  /**
   * @brief  Class to represent a point in reality.
   *
   * @note   1. Contains a coordinate and two types of speed: rectangulur one and polor one,
   *            retangulur one is used to represent movement and polor one is used to represent
   *            movement.
   *         2. Parallel class with \code line_t.
   */
  template <class T = double>
  class point_t
  {
  public:
    /* Constructor */
    point_t(){}
    point_t(T x0, T y0, T z0): coor_(x0, y0, z0){}
    point_t(T rho0, T alpha0, T beta0, int dummy): coor_(rho0, alpha0, beta0, dummy){}

    point_t(const point_t & copy): coor_(copy.coor_), speed_rect_(copy.speed_rect_), speed_polor_(copy.speed_polor_){}
    virtual ~point_t(){}

    /* Others */
    coor_t<T> get_coor() const
    {
      return coor_;
    }
    vector_t<T> get_speed_rect() const
    {
      return speed_rect_;
    }
    vector_t<T> get_speed_polor() const
    {
      return speed_polor_;
    }
    void set_speed_rect(T x0, T y0, T z0)
    {
      speed_rect_.rect().set(x0, y0, z0);
    }
    void set_speed_polor(T rho0, T alpha0, T beta0)
    {
      speed_polor_.polor().set(rho0, alpha0, beta0);
    }
    /**
     * @note  To simulate real-world, this function should only be used to initualize and after
     *         that, only use \code refresh_coor() to change coor.
     */
    void set_coor(T x0, T y0, T z0)
    {
      coor_.rect().set(x0, y0, z0);
    }
    void set_coor(T rho0, T alpha0, T beta0, int dummy)
    {
      coor_.polor().set(rho0, alpha0, beta0);
      dummy = dummy;
    }

    /**
     * @breif  To simulate real-world, this function use \code'speed_' variable to decide how to
     *         refresh the coor.
     *
     * @input  dt             Delta time, in unit of second.
     *
     * @note   1. coor_new = coor_old + speed_v*dt + speed_p*vt;
     */
    void refresh_coor(double dt)
    {
      coor_.rect().x_ += speed_rect_.rect().x_ * dt;
      coor_.rect().y_ += speed_rect_.rect().y_ * dt;
      coor_.rect().z_ += speed_rect_.rect().z_ * dt;

      coor_.polor().rho_ += speed_polor_.polor().rho_ * dt;
      coor_.polor().alpha_ += speed_polor_.polor().alpha_ * dt;
      coor_.polor().beta_ += speed_polor_.polor().beta_ * dt;
    }

  private:
    /** @note No assignment! */
    const point_t & operator=(const point_t<T> & copy){}

    coor_t<T> coor_;
    vector_t<T> speed_rect_, speed_polor_;
  };

  /**
   * @breif  Class to represent a line in reality.
   *
   * @note   1. Parallel class with point_t.
   */
  template <class T = double>
  class line_t
  {
  public:
    /* Constructor */
    line_t(){}
    line_t(const point_t<T> & p1, const point_t<T> & p2): point_beg_(p1), point_end_(p2)
    {
      vector_.rect() = point_end_.coor().rect() - point_beg_.coor().rect();
    }
    line_t(T x1, T y1, T z1, T x2, T y2, T z2): point_beg_(x1, y1, z1), point_end_(x2, y2, z2)
    {
      vector_.rect() = point_end_.get_coor().rect() - point_beg_.get_coor().rect();
    }
    line_t(const line_t & copy): point_beg_(copy.point_beg_), point_end_(copy.point_end_)
    {
      vector_.rect() = point_end_.coor().rect() - point_beg_.coor().rect();
    }
    ~line_t(){}

    /* Others */
    const point_t<T> point_beg() const{return point_beg_;}
    const point_t<T> point_end() const{return point_end_;}
    const vector_t<T> vector() const{return vector_;}
    T length() const
    {
      vector_t<T> tmp;
      tmp.rect() = point_beg_.get_coor().rect() - point_end_.get_coor().rect();
      return tmp.polor().rho_;
    }

  private:
    /** @note No assignment ! */
    const line_t & operator=(const line_t & copy){}

    point_t<T> point_beg_, point_end_;
    vector_t<T> vector_;
  };

  /**
   * @breif  Calculate the distance between two point.
   */
  template <class T>
  double cal_distance(const point_t<T> & point1, const point_t<T> & point2)
  {
    vector_t<T> v;
    v.rect() = point1.get_coor().rect() - point2.get_coor().rect();
    return v.polor().rho_;
  }

  /**
   * @breif  Calculate the distance between one point and one line.
   */
  template <class T>
  double cal_distance(const point_t<T> & point, const line_t<T> & line)
  {
    if(line.length() == 0)
    {
      return 0;
    }
    vector_t<> tmp1, tmp2, tmp3;
    tmp1.rect() = point.get_coor().rect() + line.point_beg().get_coor().rect();
    tmp2.rect() = point.get_coor().rect() + line.point_end().get_coor().rect();
    tmp3 = tmp1 ^ tmp2;
    return tmp3.polor().rho_ / line.length();
  }

  /**
   * @breif  Calculate the angle between two line.
   */
  template <class T>
  double cal_angle(const line_t<T> & line1, const line_t<T> & line2)
  {
    if((line1.length() == 0) || line2.length() == 0)
    {
      return 0;
    }
    double dp = line1.vector() * line2.vector();
    return std::acos(dp / (line1.length() *line2.length()));
  }

} // namespace basic
} // namespace shooter


#endif
